/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Copyright 2002-2003 by Mark Wolgemuth, All rights reserved.             */
/* Copyright 2002-2003 by Employease, Inc.  All rights reserved.           */
/*                                                                         */
/* Redistribution and use in source and binary forms, with or without      */
/* modification, are permitted provided that the following conditions      */
/* are met:                                                                */
/*                                                                         */
/*    1. Redistributions of source code must retain the above copyright    */
/* notice, this list of conditions and the following disclaimer.           */
/*    2. Redistributions in binary form must reproduce the above copyright */
/* notice, this list of conditions and the following disclaimer in the     */
/* documentation and/or other materials provided with the distribution.    */
/*                                                                         */
/* THIS SOFTWARE IS PROVIDED BY THE MOD_ATHENA PROJECT ``AS IS'' AND ANY   */
/* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE       */
/* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR      */
/* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR        */
/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,   */
/* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,     */
/* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR      */
/* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY     */
/* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT            */
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE   */
/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.    */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */


#include "athd.h"

/* begin_privates */
static apr_hash_t *_cache = NULL;
static apr_pool_t *_cache_ppool = NULL;
static apr_pool_t *_cache_pool = NULL;

static athd_engine * _athd_engine_alloc(apr_pool_t *pool);
static void _athd_engine_cache_expire();
static apr_status_t _athd_engine_lock(athd_engine *engine);
static apr_status_t _athd_engine_unlock(athd_engine *engine);

/* end_privates */


athd_engine *
athd_engine_create(apr_pool_t *pool, const char *name)
{
    athd_engine *engine = _athd_engine_alloc(pool);
    engine->name = apr_pstrdup(engine->pool, name);
    engine->keep_alive = 1;
    return engine;
}    

void
athd_engine_destroy(athd_engine **engine)
{
    apr_pool_t *pool;
    pool = (*engine)->pool;
    apr_pool_destroy(pool);
    (*engine) = NULL;
}

void
athd_engine_cache_init(apr_pool_t *pool)
{
    apr_pool_t *subpool;
    ASSERT(pool);
    /* always use the same parent pool we started with */
/*     if (_cache_ppool)  */
/* 	ASSERT(_cache_ppool == pool); */
/*     else */
    /* that protection breaks hup restarts */
    if (_cache_ppool)
      {
	  _athd_engine_cache_expire();
	  apr_pool_destroy(_cache_ppool);
      }
    _cache_ppool = pool;
    apr_pool_create(&subpool, _cache_ppool);
    _cache_pool = subpool;
    _cache = apr_hash_make(_cache_pool);
}

athd_engine * 
athd_engine_add_by_name(athd_conf *conf, const char *name)
{
    athd_engine *engine;

    ASSERT(conf);
    if (name)
      {
	  engine = athd_engine_for_name(conf->engines, name);
	  if (engine)
	      athd_log(ATHD_DEBUG, ATHD_WARN_ENGEXST, engine->name);
	  else
	    {
		engine = athd_engine_create(conf->pool, name);
		ath_list_ap_add(conf->pool, conf->engines, engine);
	    }
	  return engine;
      }
    return NULL;
}

athd_engine * 
athd_engine_for_name(ath_list *engines, const char *name)
{
    int i;
    athd_engine *engine = NULL;
	
    if (name)
      {
	  if (_cache)
	      engine = (athd_engine *)
		apr_hash_get(_cache, name, APR_HASH_KEY_STRING);
	  else if (_cache_ppool)
	      athd_engine_cache_init(_cache_ppool);
	  
	  if (!engine)
	    {
		ath_node *ctx;
		ath_list_rewind_r(engines, &ctx);
		while ((engine == NULL) && (ath_list_hasnext_r(engines, &ctx)))
		  {
		      engine = (athd_engine *) 
			ath_list_getnext_r(engines, &ctx);
		      if ((strlen(name) != strlen(engine->name))
			  || (strncmp(engine->name, name, strlen(name)))
			  )
			  engine = NULL;
		  }
		if ((_cache) && (engine))
		    apr_hash_set(_cache, engine->name, 
				 APR_HASH_KEY_STRING, engine);
	    }
      }
    return engine;
}

apr_status_t
athd_engine_push_req(athd_request *req)
{
    apr_status_t rv = APR_SUCCESS;
    ASSERT(req);
    ASSERT(req->engine);

    if (req->cacheable)
      {
	  /* cache logic not impl yet */
      }

    if (req->engine->refusing)
	rv = req->engine->refusing;
    else if ((ath_list_size(req->engine->request_q)) >
	     req->engine->request_q_limit)
	rv = APR_EAGAIN; /* resource temporarily unavailable */
    if (rv)
      {
	  athd_log_err(ATHD_WARN, rv, 
		       "Job [%s] Engine [%s] "
		       "Error pushing request to request queue: ",
		       req->job->name, req->engine->name);
	  athd_req_destroy(&req);
      }
    else
      {
	  /* pool is from request, so relevant memory is freed
	     when athd_req_destroy is called
	  */
	  req->expires = (apr_time_now()) 
	    + ((ATHD_DEFAULT_REQEXPIRE) * XTH_SEC(1));
	  ath_list_ap_add_tail(req->pool, req->engine->request_q, req);
      }

    return rv;
}

athd_request *
athd_engine_shift_req(athd_engine *engine)
{
    ath_node *ctx;
    athd_request *req;
    ASSERT(engine);
    ath_list_rewind_r(engine->request_q, &ctx);
    /* a list can contain a NULL */
    /* but THIS list cannot */
    if (ath_list_hasnext_r(engine->request_q, &ctx))
	req = ath_list_popnext_r(engine->request_q, &ctx);
    else
	req = NULL;
    return req;
}

int
athd_engine_req_qsize(athd_engine *engine)
{
    return ath_list_size(engine->request_q);
}

apr_status_t
athd_engine_set_url(athd_engine *engine, const char *url)
{
    int rv;
    engine->url = apr_pstrdup(engine->pool, url);
    rv = apr_uri_parse(engine->pool, url, engine->uri);
    if (!engine->uri->port)
	engine->uri->port = 
	  apr_uri_default_port_for_scheme(engine->uri->scheme);
    return rv;
}

apr_status_t
athd_engine_new_connection(athd_engine *eng)
{
    int rv;
    athd_connection *conn;
    ASSERT(eng);
    rv = athd_connection_create(&conn, eng->uri, eng->pool);
    eng->conn_current = conn;
    return rv;
}

char *
athd_engine_to_str(apr_pool_t *pool, athd_engine *eng)
{
    char *str;
    char *strsecs = NULL;
    ath_node *listctx;
    ASSERT(eng);
    ath_list_rewind_r(eng->secondaries, &listctx);
    while (ath_list_hasnext_r(eng->secondaries, &listctx))
      {
	  athd_engine *engsec = (athd_engine *)
	    ath_list_getnext_r(eng->secondaries, &listctx);
	  if (!strsecs)
	      strsecs = apr_pstrcat(pool, engsec->name, NULL);
	  else
	      strsecs = apr_pstrcat(pool, strsecs, " ", engsec->name, NULL);
      }
    str = apr_psprintf(pool, 
		       "Engine name: %s\n"
		       "  connected: %s\n"
		       "  url: %s\n"
		       "  secondaries: %s\n"
		       "  keepalive: %s\n"
		       "  refusing: %s\n"
		       "  slow factor: %d\n"
		       "  ip enabled: %s\n",
		       eng->name,
		       eng->dienow ? "no" : "yes",
		       eng->url,
		       strsecs ? strsecs : "(none)",
		       eng->keep_alive ? "yes" : "no",
		       eng->refusing ? "yes" : "no",
		       eng->slow_server_factor,
		       eng->ip_enable ? "yes" : "no");
    return str;
}

ath_phys *
athd_engine_phys_map_set(athd_engine *eng, const ath_phys *physsrc)
{
    ath_phys *phys;
    phys = eng->clone_map_phys[physsrc->idx];
    if (!phys)
	phys = ath_phys_alloc(eng->pool);
    ath_phys_copy(phys, physsrc);
    eng->clone_map_phys[physsrc->idx] = phys;
    return phys;
}

ath_farm *
athd_engine_farm_map_set(athd_engine *eng, const ath_farm *farmsrc)
{
    ath_farm *farm;
    farm = eng->clone_map_farm[farmsrc->idx];
    if (!farm)
	farm = ath_farm_alloc(eng->pool);
    ath_farm_copy(farm, farmsrc);
    eng->clone_map_farm[farmsrc->idx] = farm;
    return farm;
}

ath_farm *
athd_engine_farm_map_get(athd_engine *eng, const char *name)
{
    ath_farm *farm;
    int i;
    ASSERT(name);
    for (i = 0; eng->clone_map_farm[i] != NULL; i++)
      {
	  farm = eng->clone_map_farm[i];
	  if (!strcmp(name, farm->name))
	      break;
	  else
	      farm = NULL;
      }
    return farm;
}
/* privates */

static athd_engine * 
_athd_engine_alloc(apr_pool_t *pool)
{
    athd_engine *engine;
    apr_pool_t *epool;
    int i;
    apr_pool_create(&epool, pool);
    engine = (athd_engine *) 
      apr_pcalloc(epool, sizeof(athd_engine));
    engine->pool = epool;
    engine->url = NULL;
    engine->secondaries = ath_list_ap_alloc(engine->pool);
    engine->request_q = ath_list_ap_alloc(engine->pool);
    engine->request_q_limit = ATHD_DEFAULT_QLIMIT;
    engine->uri = apr_pcalloc(engine->pool, sizeof(apr_uri_t));
    engine->mutex = apr_pcalloc(engine->pool, sizeof(pthread_mutex_t));
    apr_pool_cleanup_register(engine->pool, engine->mutex,
			      pthread_mutex_cleanup, apr_pool_cleanup_null);
    pthread_mutex_init(engine->mutex, NULL);
    engine->thread = NULL;
    engine->conn_current = NULL;
    engine->refusing = 0;
    engine->dienow = 0;
    engine->ip_enable = 0;
    engine->slow_server_factor = 1;
    for (i = 0; i < ATH_PHYS_HARDMAX + 1; i++)
	engine->clone_map_phys[i] = NULL;
    for (i = 0; i < ATH_FARM_HARDMAX + 1; i++)
	engine->clone_map_farm[i] = NULL;
    engine->clone_map_farm_mtime = 0;
    engine->clone_map_phys_mtime = 0;
    engine->clone_map_expire = ATHD_CMD_MAP_EXPIRE;
    return engine;	
}

static void 
_athd_engine_cache_expire()
{
    if (_cache)
      {
	  if (_cache_pool)
	      apr_pool_destroy(_cache_pool);
	  _cache_pool = NULL;
      }
    _cache = NULL;
}

static apr_status_t
_athd_engine_lock(athd_engine *engine)
{
    ASSERT(engine);
    return pthread_mutex_lock(engine->mutex);
}

static apr_status_t
_athd_engine_unlock(athd_engine *engine)
{
    ASSERT(engine);
    return pthread_mutex_unlock(engine->mutex);
}
